
#include "StrVec.hpp"

#include <iostream>
#include <memory>
// alloc must be defined in the StrVec implementation file
std::allocator<std::string> StrVec::alloc;

/**
 * 重要方法
 * 构造一段新的空间，用于分配空间，存放新的复制过来的对象
 * 用于拷贝构造函数和拷贝赋值运算符
 * @param b 指向资源头部的指针
 * @param e 指向资源尾部的指针
 * @return pair类型，方便赋值
 */
std::pair<std::string*, std::string*> StrVec::allocateAndCopy
    (const std::string* b, const std::string* e) {
    // allocate space to hold as many elements as are in the range
    auto data = alloc.allocate(e - b);
    // initialize and return a pair constructed from data and
    // the value returned by uninitialized_copy
    // 这里会调用拷贝构造
    return {data, std::uninitialized_copy(b, e, data)};
}

void StrVec::free()
{
    // may not pass deallocate a 0 pointer; if elements is 0, there's no work to do
    if (elements) {
        // destroy the old elements in reverse order
        for (auto p = first_free; p != elements; /* empty */)
            alloc.destroy(--p); // 这里会调用std::string的析构函数
        alloc.deallocate(elements, cap - elements);
    }
}

StrVec::~StrVec() { free(); }

StrVec::StrVec(const StrVec &s)
{
    // call alloc_n_copy to allocate exactly as many elements as in s
    auto newdata = allocateAndCopy(s.begin(), s.end());
    elements = newdata.first;
    first_free = cap = newdata.second;
}

StrVec::StrVec(std::initializer_list<std::string> il) {
    auto data = allocateAndCopy(il.begin(), il.end());
    elements = data.first; // update data members to point to the new space
    first_free = cap = data.second;
}

StrVec &StrVec::operator=(const StrVec &rhs)
{
    // call alloc_n_copy to allocate exactly as many elements as in rhs
    auto data = allocateAndCopy(rhs.begin(), rhs.end());
    free();
    elements = data.first;
    first_free = cap = data.second;
    return *this;
}

/**
 *
 */
void StrVec::reallocate()
{// we'll allocate space for twice as many elements as the current size
    // size() = 0的情况，是当前类未被分配string
    auto newcapacity = size() ? 2 * size() : 1;
    // allocate new memory
    auto newdata = alloc.allocate(newcapacity);
    // move the data from the old memory to the new
    auto dest = newdata; // points to the next free position in the new array
    auto elem = elements; // points to the next element in the old array

    // construct第二个参数可以使用移动构造，避免复制
    for (size_t i = 0; i != size(); ++i)
        alloc.construct(dest++, std::move(*elem++));
    free(); // free the old space once we've moved the elements
    // update our data structure to point to the new elements
    elements = newdata;
    first_free = dest;
    cap = elements + newcapacity;
}

StrVec::StrVec(StrVec &&s) noexcept // move won't throw any exceptions
// member initializers take over the resources in s
: elements(s.elements), first_free(s.first_free), cap(s.cap)
{
    // leave s in a state in which it is safe to run the destructor
    s.elements = s.first_free = s.cap = nullptr;
    std::cout << "移动构造" << std::endl;
}

StrVec &StrVec::operator=(StrVec &&rhs) noexcept
{
    std::cout << "移动赋值" << std::endl;
    // direct test for self-assignment
    if (this != &rhs) {
        free(); // free existing elements
        elements = rhs.elements; // take over resources from rhs
        first_free = rhs.first_free;
        cap = rhs.cap;
        // leave rhs in a destructible state
        rhs.elements = rhs.first_free = rhs.cap = nullptr;
    }
    return *this;
}

/**
 * 区别于move assignment和copy assignment的operator=的重载
 * @param il 临时数组，当方法返回时就销毁了，因此需要将它的值拷贝下来
 * @return
 */
StrVec &StrVec::operator=(std::initializer_list<std::string> il) {
        // alloc_n_copy allocates space and copies elements from the given range
    // il.begin()返回的就是const string*
    auto data = allocateAndCopy(il.begin(), il.end());
    free(); // destroy the elements in this object and free the space
    elements = data.first; // update data members to point to the new space
    first_free = cap = data.second;
    return *this;
}

std::string & StrVec::operator[](std::size_t n) {
    if (n >= size()) {
        throw std::out_of_range("StrVec::operator[]");
    }
    return elements[n];
}

const std::string & StrVec::operator[](std::size_t n) const {
    if (n >= size()) {
        throw std::out_of_range("StrVec::operator[]");
    }
    return elements[n];
}



void StrVec::push_back(const std::string& s)
{
    checkAndAllocate(); // ensure that there is room for another element
    // construct a copy of s in the element to which first_free points
    alloc.construct(first_free++, s);
}

std::ostream& operator<<(std::ostream& os, const StrVec& strVec) {
    if (strVec.size() == 0) {
        return os;
    }
    os << strVec[0];
    for (size_t i = 1; i < strVec.size(); ++i) {
        os << std::string(" ") << strVec[i];
    }
    return os;
}